home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer)…68k, x86, SPARC, PA-RISC] / NeXTSTEP 3.3 Dev Intel.iso / NextDeveloper / Examples / DriverKit / Adaptec1542B / Adaptec1542B_reloc.tproj / AHAController.m < prev    next >
Encoding:
Text File  |  1995-02-10  |  14.9 KB  |  628 lines

  1. /*
  2.  * Copyright (c) 1992, 1993 NeXT Computer, Inc.
  3.  *
  4.  * Adaptec AHA-1542 SCSI controller driver.
  5.  *
  6.  * HISTORY
  7.  *
  8.  * 13 Apr 1993    Doug Mitchell at NeXT
  9.  *    Rewrote using I/O thread which has exclusive access to hardware.
  10.  *
  11.  * 22 Nov 1992     Brian Pinkerton at NeXT
  12.  *    Created from non-driverkit driver.
  13.  */
  14.  
  15. #import <sys/types.h>
  16. #import <bsd/sys/param.h>
  17. #import <objc/Object.h>
  18. #import <kernserv/queue.h>
  19. #import <kernserv/prototypes.h>
  20. #import <driverkit/return.h>
  21. #import <driverkit/generalFuncs.h>
  22. #import <driverkit/kernelDriver.h>
  23. #import <driverkit/i386/kernelDriver.h>
  24. #import <driverkit/interruptMsg.h>
  25. #import <driverkit/scsiTypes.h>
  26. #import <bsd/dev/scsireg.h>
  27. #import    "scsivar.h"
  28. #import <mach/message.h>
  29. #import <mach/port.h>
  30. #import <mach/mach_interface.h>
  31. #import <machkit/NXLock.h>
  32. #import <kernserv/ns_timer.h>
  33. #import <driverkit/i386/ioPorts.h>
  34.  
  35. #import <driverkit/i386/directDevice.h>
  36. #import <driverkit/i386/IOEISADeviceDescription.h>
  37. #import <driverkit/IOSCSIController.h>
  38. #import "AHAController.h"
  39. #import "AHATypes.h"
  40. #import "AHAInline.h"
  41. #import "AHAThread.h"
  42.  
  43. extern unsigned ffs(unsigned mask);
  44.  
  45. /* 
  46.  * Template for command message sent to the I/O thread.
  47.  */
  48. static msg_header_t AHAMessageTemplate = {
  49.     0,                    // msg_unused 
  50.     1,                    // msg_simple 
  51.     sizeof(msg_header_t),            // msg_size 
  52.     MSG_TYPE_NORMAL,            // msg_type 
  53.     PORT_NULL,                // msg_local_port 
  54.     PORT_NULL,                // msg_remote_port - TO
  55.                         // BE FILLED IN 
  56.     IO_COMMAND_MSG                // msg_id 
  57. };
  58.  
  59. /*
  60.  * Private methods implemented in this file.
  61.  */
  62. @interface AHAController(PrivateMethods)
  63. - (BOOL) probeAtPortBase     : (IOEISAPortAddress) portBase;
  64. - (IOReturn)executeCmdBuf    : (AHACommandBuf *)cmdBuf;
  65. @end
  66.  
  67.  
  68. @implementation AHAController
  69.  
  70. /*
  71.  *  Probe, configure board, and init new instance.
  72.  */
  73. + (BOOL)probe:deviceDescription
  74. {
  75.     AHAController    *aha = [self alloc];
  76.     IORange        ioPort;
  77.  
  78.     ddm_init("AHAController probe\n", 1,2,3,4,5);
  79.     aha->ioThreadRunning = NO;
  80.     
  81.     /*
  82.      *  Check that we have some IO Ports assigned, and probe using the
  83.      *  first IO Port.
  84.      *  -probeAtPortBase returns TRUE if there's an AHA Controller present.
  85.      */
  86.     if ([deviceDescription numPortRanges] < 1) {
  87.         IOLog("AHAController: can't determine port base!\n");
  88.             [aha free];
  89.         return NO;
  90.     }
  91.     ioPort = [deviceDescription portRangeList][0];
  92.     if (![aha probeAtPortBase:ioPort.start]) {
  93.         IOLog("Adaptec154x Not Found at port 0x%x\n", ioPort.start);
  94.             [aha free];
  95.         return NO;
  96.     }
  97.     return ([aha initFromDeviceDescription:deviceDescription] ? YES : NO);
  98. }
  99.  
  100. - initFromDeviceDescription:deviceDescription
  101. {
  102.     unsigned Lun;
  103.     kern_return_t krtn;
  104.     
  105.     ddm_init("AHAController initFromDeviceDescription\n", 1,2,3,4,5);
  106.     
  107.     queue_init(&outstandingQ);
  108.     queue_init(&pendingQ);
  109.     queue_init(&commandQ);
  110.     commandLock      = [[NXLock alloc] init];
  111.     outstandingCount = 0;
  112.     dmaLockCount     = 0;
  113.     numFreeCcbs      = AHA_QUEUE_SIZE;
  114.  
  115.     /*
  116.      * Note the I/O thread provided by IOSCSIController is running 
  117.      * upon return from the following method.
  118.      */
  119.     if ([super initFromDeviceDescription:deviceDescription] == nil)
  120.         return [self free];
  121.     interruptPortKern = IOConvertPort([self interruptPort],
  122.         IO_KernelIOTask,
  123.         IO_Kernel);
  124.     ioThreadRunning = YES;
  125.     
  126.     /*
  127.      *  Check the channel and irq we just found against what's in our
  128.      *  device description.  If they don't match, print a nasty warning
  129.      *  message and fail.
  130.      */
  131.     if ([deviceDescription numChannels] < 1 ||
  132.         [deviceDescription channel] != config.dma_channel) {
  133.         IOLog("AHAController: Actual DMA Channel (%d) doesn't match "
  134.               "configured value (%d)!\n", config.dma_channel,
  135.               ([deviceDescription numChannels] ? 
  136.                   [deviceDescription channel] : 0));
  137.         return [self free];
  138.     }
  139.  
  140.     if ([deviceDescription numInterrupts] < 1 ||
  141.         [deviceDescription interrupt] != config.irq) {
  142.         IOLog("AHAController: Actual IRQ (%d) doesn't match "
  143.               "configured value (%d)!\n", config.irq,
  144.               ([deviceDescription numInterrupts] ?
  145.                   [deviceDescription interrupt] : 0));
  146.         return [self free];
  147.     }
  148.     
  149.     /*
  150.      *  Try to set up DMA.  Set the appropriate mode on the channel, and
  151.      *  try to enable it.
  152.      */
  153.     if ([self setTransferMode:IO_Cascade forChannel:0] != IO_R_SUCCESS ||
  154.         [self enableChannel:0] != IO_R_SUCCESS) {
  155.         IOLog("AHAController: couldn't init DMA!\n");
  156.         return [super free];
  157.     }
  158.  
  159.     /*
  160.      * Allocate Mailboxes and CCB's from low 16 M of memory.
  161.      */
  162.     ahaMbArea = IOMallocLow(sizeof(struct aha_mb_area));
  163.     ahaCcb = IOMallocLow(sizeof(struct ccb) * AHA_QUEUE_SIZE);
  164.  
  165.     /*
  166.      *  Initialize driver data structures: set up the mailbox in/out area,
  167.      *  and initialize the CCB queues.
  168.      *
  169.      *  Note that if we fail, the call to [super free] will release (and
  170.      *  disable) our resources (IRQ, DMA channel, portRanges).
  171.      */
  172.     if (!aha_setup_mb_area(ioBase, ahaMbArea, ahaCcb)) {
  173.         IOLog("AHAController: couldn't set up mailbox area!\n");
  174.         return [self free];
  175.     }
  176.  
  177.     [self resetStats];
  178.     
  179.     /*
  180.      * Reserve our target, enable interrupts, and go.
  181.      */
  182.     for(Lun=0; Lun<SCSI_NLUNS; Lun++) {
  183.         [self reserveTarget:config.scsi_id lun:Lun forOwner:self];
  184.     }
  185.  
  186.     [self enableAllInterrupts];    /* turn on interrupts */
  187.         
  188.     /*
  189.      * Set the port queue length to the maximum size. 
  190.      */
  191.     krtn = port_set_backlog(task_self(), [self interruptPort], 
  192.         PORT_BACKLOG_MAX);
  193.     if(krtn) {
  194.         IOLog("%s: error %d on port_set_backlog()\n",
  195.             [self name], krtn);
  196.         /* Oh well... */
  197.     }
  198.     [self resetSCSIBus];
  199.     [self registerDevice];        /* this is the last thing we do! */
  200.  
  201.     return self;
  202. }
  203.  
  204. /*
  205.  *  This is slightly incorrect, since we can actually handle more if some of
  206.  *  the entries in the scatter/gather list can be more than PAGE_SIZE.  In
  207.  *  practice, tho, they're never bigger, so we'll make this our max size.
  208.  *  18 Mar 93 dmitch - use (AHA_SG_COUNT - 1) since requests (the first
  209.  *  and the last) can cross page boundaries. 
  210.  */
  211. - (unsigned)maxTransfer
  212. {
  213.     return (AHA_SG_COUNT - 1) * PAGE_SIZE;
  214. }
  215.  
  216. /* 
  217.  * kill I/O thread, free up local dynamically allocated resources, 
  218.  * then have super release resources. 
  219.  */
  220. - free
  221. {
  222.     AHACommandBuf cmdBuf;
  223.     
  224.     if(ioThreadRunning) {
  225.         cmdBuf.op = AO_Abort;
  226.         [self executeCmdBuf:&cmdBuf];
  227.     }
  228.     if(ahaMbArea) {
  229.         IOFreeLow(ahaMbArea, sizeof(struct aha_mb_area));
  230.     }
  231.     if(ahaCcb) {
  232.         IOFreeLow(ahaCcb, sizeof(struct ccb) * AHA_QUEUE_SIZE);
  233.     }
  234.     if(commandLock) {
  235.         [commandLock free];
  236.     }
  237.     return [super free];
  238. }
  239.  
  240. /*
  241.  * Statistics support.
  242.  */
  243. - (unsigned int) numQueueSamples
  244. {
  245.     return totalCommands;
  246. }
  247.  
  248.  
  249. - (unsigned int) sumQueueLengths
  250. {
  251.     return queueLenTotal;
  252. }
  253.  
  254.  
  255. - (unsigned int) maxQueueLength
  256. {
  257.     return maxQueueLen;
  258. }
  259.  
  260.  
  261. - (void)resetStats
  262. {
  263.     totalCommands = 0;
  264.     queueLenTotal = 0;
  265.     maxQueueLen   = 0;
  266. }
  267.  
  268. /*
  269.  * Do a SCSI command, as specified by an IOSCSIRequest. All the 
  270.  * work is done by the I/O thread.
  271.  */
  272. - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq 
  273.             buffer : (void *)buffer 
  274.             client : (vm_task_t)client
  275. {
  276.     AHACommandBuf cmdBuf;
  277.     
  278.     ddm_exp("executeRequest: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
  279.     
  280.     cmdBuf.op      = AO_Execute;
  281.     cmdBuf.scsiReq = scsiReq;
  282.     cmdBuf.buffer  = buffer;
  283.     cmdBuf.client  = client;
  284.     
  285.     [self executeCmdBuf:&cmdBuf];
  286.     
  287.     ddm_exp("executeRequest: cmdBuf 0x%x complete; result %d\n", 
  288.         &cmdBuf, cmdBuf.result, 3,4,5);
  289.     return cmdBuf.result;
  290. }
  291.  
  292.  
  293. /*
  294.  *  Reset the SCSI bus. All the work is done by the I/O thread.
  295.  */
  296. - (sc_status_t)resetSCSIBus
  297. {
  298.     AHACommandBuf cmdBuf;
  299.     
  300.     ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
  301.  
  302.     cmdBuf.op = AO_Reset;
  303.     [self executeCmdBuf:&cmdBuf];
  304.     return cmdBuf.result;
  305. }
  306. /*
  307.  * The following 6 methods are all called from the I/O thread in 
  308.  * IODirectDevice. 
  309.  */
  310.  
  311. /*
  312.  * Called from the I/O thread when it receives an interrupt message.
  313.  */
  314. - (void)interruptOccurred
  315. {
  316.     struct ccb    *ccb;
  317.     aha_intr_reg_t    intr;
  318.     aha_mb_t    *mb;
  319.     int        i;
  320.  
  321.     ddm_thr("interruptOccurred\n", 1,2,3,4,5);
  322.  
  323.     intr = aha_get_intr(ioBase);
  324.     aha_clr_intr(ioBase);
  325.  
  326.     if (!intr.mb_in_full)
  327.         return;
  328.  
  329.     /*
  330.      * Find all ccb's which the controller has marked completed
  331.      * and commandComplete: them.
  332.      */
  333.     mb = ahaMbArea->mb_in;
  334.     for (i = 0; i < AHA_MB_CNT; i++, mb++) {
  335.         if (mb->mb_stat != AHA_MB_IN_FREE) {
  336.             
  337.             /*
  338.              * FIXME - need IOVirtualFromPhysical(); assume for
  339.              * now that we can access all physical addresses.
  340.              */
  341.             ccb = (struct ccb *)aha_get_24(mb->ccb_addr);
  342.             mb->mb_stat = AHA_MB_IN_FREE;    
  343.             queue_remove(&outstandingQ, ccb, struct ccb *, ccbQ);
  344.             ASSERT(outstandingCount != 0);
  345.             outstandingCount--;
  346.             
  347.             [self commandCompleted:ccb reason:CS_Complete];
  348.         }
  349.     }
  350.     
  351.     /*
  352.      * Handle possible pending commands (now that we've dequeued at least
  353.      * one CCB).
  354.      */
  355.     [self runPendingCommands];
  356.     
  357.     /*
  358.      * One more thing - since we probably just freed up at least one
  359.      * ccb, process possible entries waiting in commandQ.
  360.      */
  361.     [self commandRequestOccurred]; 
  362.     ddm_thr("interruptOccurred: DONE\n", 1,2,3,4,5);
  363. }
  364.  
  365. /*
  366.  * These three should not occur; they are here as error traps. All three are 
  367.  * called out from the I/O thread upon receipt of messages which it should
  368.  * not be seeing.
  369.  */
  370. - (void)interruptOccurredAt:(int)localNum
  371. {
  372.     IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum);
  373. }
  374.  
  375. - (void)otherOccurred:(int)id
  376. {
  377.     IOLog("%s: otherOccurred:%d\n", [self name], id);
  378. }
  379.  
  380. - (void)receiveMsg
  381. {
  382.     IOLog("%s: receiveMsg\n", [self name]);
  383.     
  384.     /*
  385.      * We have to let IODirectDevice take care of this (i.e., dequeue the
  386.      * bogus message).
  387.      */
  388.     [super receiveMsg];
  389. }
  390.  
  391. /*
  392.  * Called from the I/O thread when it receives a timeout
  393.  * message. We send these messages ourself from ahaTimeout() in 
  394.  * AHAThread.m.
  395.  */
  396. - (void)timeoutOccurred
  397. {
  398.     struct ccb    *ccb, *nextCcb;
  399.     ns_time_t    now;
  400.     queue_head_t    *queue;
  401.     BOOL        ccbTimedOut = NO;
  402.     AHACommandBuf    *cmdBuf;
  403.     IOSCSIRequest    *scsiReq;
  404.     
  405.     ddm_thr("timeoutOccurred\n", 1,2,3,4,5);
  406.     
  407.     IOGetTimestamp(&now);
  408.  
  409.     /*
  410.      *  Scan the list of outstanding and pending commands, and time
  411.      *  out any ones whose time is past.
  412.      */
  413.  
  414.     for (queue = &outstandingQ; queue != &pendingQ; queue = &pendingQ) {
  415.  
  416.         ccb = (struct ccb *) queue_first(&outstandingQ);
  417.         while (!queue_end(&outstandingQ, (queue_entry_t) ccb)) {
  418.             ns_time_t    expire;
  419.             
  420.         cmdBuf  = ccb->cmdBuf;
  421.         scsiReq = cmdBuf->scsiReq;
  422.         expire = ccb->startTime + 
  423.             1000000000ULL * 
  424.                 (unsigned long long)scsiReq->timeoutLength;
  425.             if (now >= expire) {
  426.             /*
  427.              *  Remove ccb from the oustanding queue and
  428.              *  complete it.
  429.              */
  430.             nextCcb = (struct ccb *) queue_next(&ccb->ccbQ);
  431.             queue_remove(&outstandingQ, ccb, struct ccb *, ccbQ);
  432.             if(queue == &outstandingQ) {
  433.                 ASSERT(outstandingCount != 0);
  434.                 outstandingCount--;
  435.             }
  436.             [self commandCompleted:ccb reason:CS_Timeout];
  437.             ccb = nextCcb;
  438.             ccbTimedOut = YES;
  439.         }
  440.         else {
  441.             ccb = (struct ccb *) queue_next(&ccb->ccbQ);
  442.         }
  443.         }
  444.     }
  445.  
  446.     /*
  447.      * Reset bus. This also completes all I/Os in outstandingQ with
  448.      * status CS_Reset.
  449.      */
  450.     if(ccbTimedOut) {
  451.         [self threadResetBus:NULL];
  452.     }
  453.     ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5);
  454. }
  455.  
  456. /*
  457.  * Process all commands in commandQ. If we run out of ccb's during this 
  458.  * method, we abort, leaving commands enqueued; these will be handled after
  459.  * subqueuent interrupts.
  460.  *
  461.  * This is called either as a result of an IO_COMMAND_MSG message being 
  462.  * received by the I/O thread, or upon completion of interrupt handling. In
  463.  * either case, it runs in the context of the I/O thread.
  464.  */
  465. - (void)commandRequestOccurred
  466. {
  467.     AHACommandBuf *cmdBuf;
  468.     
  469.     ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5);
  470.     [commandLock lock];
  471.     while(!queue_empty(&commandQ)) {
  472.         cmdBuf = (AHACommandBuf *) queue_first(&commandQ);
  473.         queue_remove(&commandQ, cmdBuf, AHACommandBuf *, link);
  474.         [commandLock unlock];
  475.         switch(cmdBuf->op) {
  476.             case AO_Reset:
  477.                 [self threadResetBus:cmdBuf];
  478.             break;
  479.             
  480.             case AO_Abort:
  481.             /*
  482.              * First notify caller of completion, then 
  483.              * self-terminate.
  484.              */
  485.             [cmdBuf->cmdLock lock];
  486.             [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
  487.             IOExitThread();
  488.             /* not reached */
  489.             
  490.             case AO_Execute:
  491.                 if([self threadExecuteRequest:cmdBuf]) {
  492.                 /*
  493.                  * No more CCBs available. Abort this entire
  494.                  * method. Enqueue this request on the head
  495.                  * of commandQ for future processing.
  496.                  */
  497.                 [commandLock lock];
  498.                 queue_enter_first(&commandQ, cmdBuf,
  499.                     AHACommandBuf *, link);
  500.                 [commandLock unlock];
  501.                 ddm_thr("processCommandQ: no more ccbs; "
  502.                     "cmdBuf 0x%x\n", cmdBuf, 2,3,4,5);
  503.                 goto out;
  504.                     
  505.             }
  506.         }
  507.         [commandLock lock];
  508.     }
  509.     [commandLock unlock];
  510. out:
  511.     ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5);
  512.     return;
  513. }
  514.  
  515.  
  516. @end    /* methods declared in AHAController.h */
  517.  
  518. @implementation AHAController(PrivateMethods)
  519.  
  520. - (BOOL) probeAtPortBase:(IOEISAPortAddress) portBase
  521. {
  522.     aha_inquiry_t    inquiry;
  523.     
  524.     ddm_init("AHAController probeAtPortBase\n", 1,2,3,4,5);
  525.  
  526.     ioBase = portBase;
  527.     aha_reset_board(ioBase, ahaBoardId);
  528.  
  529.     /*
  530.      *  Do an inquiry to find out the board id and other things that
  531.      *  we won't check.
  532.      */
  533.     if (!aha_probe_cmd(ioBase, AHA_CMD_DO_INQUIRY, 0, 0,
  534.         (unsigned char *)&inquiry, sizeof(inquiry), TRUE)) {
  535.             ddm_init("  ..inquiry command failed\n", 1,2,3,4,5);
  536.         return FALSE;
  537.     }
  538.     
  539.     ahaBoardId = inquiry.board_id;
  540.  
  541.     switch (ahaBoardId) {
  542.  
  543.     case AHA_1540_16HEAD:
  544.     case AHA_154xB:
  545.     case AHA_1540_64HEAD:
  546.     case AHA_1640:
  547.     case AHA_174xA:
  548.     case AHA_154xC:
  549.         break;
  550.     default:
  551.         if (ahaBoardId < AHA_154xC) {
  552.                 ddm_init("..bogus board ID (0x%x)\n", ahaBoardId, 
  553.                 2,3,4,5);
  554.             return FALSE;
  555.         }
  556.     }
  557.  
  558.     /*
  559.      *  Attempt to read the configuration data from the board.
  560.      *  If this succeeds, then we have successfully probed.
  561.      */
  562.     if (!aha_probe_cmd(ioBase, AHA_CMD_GET_CONFIG, 0, 0,
  563.                        (unsigned char *)&config, sizeof(config), TRUE)) {
  564.             ddm_init("  ..get config command failed\n", 1,2,3,4,5);
  565.  
  566.             return FALSE;
  567.     }
  568.     
  569.     /*
  570.      *  Decode the values in the config struct.
  571.      */
  572.     config.irq = ffs((unsigned int) config.irq) + 8;
  573.     config.dma_channel = ffs((unsigned int) config.dma_channel) - 1;
  574.  
  575.     IOLog("Adaptec154x at port 0x%x irq %d\n", 
  576.         portBase, config.irq);
  577.     return TRUE;
  578. }
  579.  
  580. /*
  581.  * Pass one AHACommandBuf to the I/O thread; wait for completion. 
  582.  * Normal completion status is in cmdBuf->status; a non-zero return 
  583.  * from this function indicates a Mach IPC error.
  584.  *
  585.  * This method allocates and frees cmdBuf->cmdLock.
  586.  */
  587. - (IOReturn)executeCmdBuf : (AHACommandBuf *)cmdBuf
  588. {
  589.     msg_header_t msg = AHAMessageTemplate;
  590.     kern_return_t krtn;
  591.     IOReturn rtn = IO_R_SUCCESS;
  592.     
  593.     cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING];
  594.     [commandLock lock];
  595.     queue_enter(&commandQ, cmdBuf, AHACommandBuf *, link);
  596.     [commandLock unlock];
  597.     
  598.     /*
  599.      * Create a Mach message and send it in order to wake up the 
  600.      * I/O thread.
  601.      */
  602.     msg.msg_remote_port = interruptPortKern;
  603.     krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
  604.     if(krtn) {
  605.         IOLog("%s: msg_send_from_kernel() returned %d\n", 
  606.             [self name], krtn);
  607.         rtn = IO_R_IPC_FAILURE;
  608.         goto out;
  609.     }
  610.     
  611.     /*
  612.      * Wait for I/O complete.
  613.      */
  614.     ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n",
  615.         cmdBuf, 2,3,4,5);
  616.     [cmdBuf->cmdLock lockWhen:CMD_COMPLETE];
  617.     ddm_exp("executeCmdBuf: cmdBuf 0x%x complete\n",
  618.         cmdBuf, 2,3,4,5);
  619. out:
  620.     [cmdBuf->cmdLock free];
  621.     return rtn;
  622. }
  623.  
  624. @end    /* AHAController(PrivateMethods) */
  625.  
  626.  
  627.  
  628.